import Wx from "../../framework/wx/Wx";
import {HttpResponse} from "../entity/HttpResponse";
import Common from "../../framework/utils/Common";
import {md5} from "../utils/md5";
import Base64 from "../../framework/utils/Base64";
import {API} from "./ServerConfig";
import {userProfile} from "../entity/UserProfile";

class TimerChecker {
    private lastTick: number = 0;
    private readonly minSpanMills: number = 0;
    private userData = {};

    constructor(minSpanMills: number) {
        this.minSpanMills = minSpanMills;
    }

    public getUserData(): any {
        return this.userData;
    }

    // 判断是否为空.
    public isEmpty(): boolean {
        return !this.userData || Object.keys(this.userData).length <= 0;
    }

    public setUserData(data: any) {
        this.userData = data || {};
    }

    // 判断时间差是否大于一个限定值
    public checkAndSet(): boolean {
        const ret = cc.sys.now() - this.lastTick > this.minSpanMills;
        if (ret) {
            this.lastTick = cc.sys.now();
        }
        return ret;
    }
}

export class WxApi {

    private readonly token: string = "";
    private readonly loginResponse: any = {};

    /**
     *  通用数据存储.
     */
    private updateUserData: TimerChecker = new TimerChecker(60000);

    // ================================private =================
    private readonly _userDataCache = {};
    private _lastTimerId: number = 0;

    /**
     * 微信登录接口.
     * @param wxCode
     */
    public async login(wxCode: string): Promise<HttpResponse> {

        if (!API.authUrl) {
            return Promise.resolve(null);
        }
        if (!Wx.isWeChatEnv()) {
            return this.loginH5();
        }

        const url = API.authUrl + "/wx/auth";
        return Wx.post(
            url, {"Content-Type": "application/x-www-form-urlencoded", "app": API.app},
            {code: wxCode, channel: 0}
        ).then(resp => {
            // 特殊处理了一下token的信息. 不需要在外边额外处理了.
            if (resp.code === 0) {
                if (resp.data && resp.data["token"]) {
                    // @ts-ignore
                    this.token = resp.data["token"];
                }
                // @ts-ignore.
                this.loginResponse = resp.data
            }
            return Promise.resolve(resp);
        });
    }

    public async loginH5(): Promise<HttpResponse> {
        if (!API.authUrl) {
            return Promise.resolve(null);
        }
        const url = API.authUrl + "/wx/h5/auth2";
        const user = userProfile.userId || "" + (20000000 + Math.round(Math.random() * 10000001));
        const password = userProfile.password || "" + (80000000 + Math.round(Math.random() * 100300107));
        if (!userProfile.userId) {
            // 新玩家,就生成新的用户ID和密码.并存储.
            userProfile.userId = user;
            userProfile.password = md5(password);
        }
        return Wx.post(
            url, {"Content-Type": "application/x-www-form-urlencoded", "app": API.app},
            {platform: "h5", userId: user, password: password}
        ).then(resp => {
            // 特殊处理了一下token的信息. 不需要在外边额外处理了.
            if (resp.code === 0) {
                if (resp.data && resp.data["token"]) {
                    // @ts-ignore
                    this.token = resp.data["token"];
                }
                // @ts-ignore.
                this.loginResponse = resp.data
            }
            return Promise.resolve(resp);
        });
    }

    /**
     * 获取登录结果.
     */
    public getLoginResponse(): any {
        return this.loginResponse
    }

    /**
     * 判断是否已登录过了.
     */
    public isLogined(): boolean {
        return this.token && this.token.length > 0;
    }

    /**
     * 获取服务器时间戳.
     */
    public async time(): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/time";
        return Wx.post(url, {}, {});
    }

    /**
     * 上传玩家信息内容.
     */
    public async updateUserInfo(nickName: string, avatarUrl: string,
                                gender?: number, country?: string, province?: string, city?: string): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/updateUserInfo";
        const header = this.getAuthorHeader();
        const body = {nickName, avatarUrl, gender, country, province, city};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 获取玩家登录天数信息.
     * {
     *     "newUser": true,
     *     "loginDays": 0,
     *     "seriesDays": 0,
     *    "todaySigned": false
     * }
     */
    public async checkSignDays(): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/signDays";
        const header = this.getAuthorHeader();
        const body = {};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 玩家手动签到.
     * 签到成功,应当返回已签到多少天.
     */
    // {
    //     "code": 0,
    //     "errMsg": "OK",
    //     "data": {
    //         "loginDays": 1,
    //         "seriesDays": 1,
    //         "signResult": 0
    //     }
    // }
    public async signIn(): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/checkin";
        const header = this.getAuthorHeader();
        const body = {};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 上传排行榜信息.
     * @param rankData: {score:1000}
     * @param sortKey
     * @param mode 0/1/2/3= 全局排行/按周重置/按月重置/按年重置
     * @param sortOrder
     * @param forceUpdate
     */
    public async uploadRank(rankData: any, sortKey: string, mode: number = 0,
                            sortOrder: "ASC" | "DESC" = "DESC",
                            forceUpdate: boolean = true,): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/uploadRank";
        const header = this.getAuthorHeader();
        const body = {
            data: JSON.stringify(rankData),
            mode, sortKey, sortOrder, forceUpdate
        };
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 获取全国排行数据.
     * 需要分页请求,不能一次请求太多.
     * @param page
     * @param size
     * @param sortKey
     * @param order
     * @param mode
     */
    public async getGlobalRank(page: number = 0, size: number = 100,
                               sortKey: string,
                               order: "DESC" | "ASC" = "DESC", mode: number = 0): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/globalRank";
        const header = this.getAuthorHeader();
        const body = {page, size, sortKey, order, mode};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     *  通用数据存储.
     *  @param kvs = {key1:value1,key2:value2}
     */
    public async setUserData(kvs: any): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        Common.assign(this.updateUserData.getUserData(), kvs);
        if (!this.updateUserData.checkAndSet()) {
            return Promise.resolve(null);
        }
        const kvList = JSON.stringify(this.updateUserData.getUserData());
        this.updateUserData.setUserData({});
        const url = API.baseUrl + "/wx/api/setUserData";
        const header = this.getAuthorHeader();
        const body = {KVList: kvList};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    // 强制上传用户数据.
    public async notifyUploadUserData(): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        if (this.updateUserData.isEmpty()) {
            return;
        }
        cc.log("向上同步服务器数据.");
        const kvList = JSON.stringify(this.updateUserData.getUserData());
        this.updateUserData.setUserData({});
        const url = API.baseUrl + "/wx/api/setUserData";
        const header = this.getAuthorHeader();
        const body = {KVList: kvList};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 获取线上数据内容.
     * @param keys
     */
    public async getUserData(keys: string[]): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/getUserData";
        const header = this.getAuthorHeader();
        const body = {keys};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    public getUserDataSafe(key: string): Promise<HttpResponse> {
        return new Promise<HttpResponse>(resolve => {
            this.getUserDataWithInterval(key, (resp) => {
                resolve(resp)
            })
        })
    }

    /**
     * 清除存储数据.
     * @param keys
     */
    public async deleteUserData(...keys: string[]): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/deleteUserData";
        const header = this.getAuthorHeader();
        const body = {keys};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 解析群信息.获取群ID.
     * @param iv
     * @param data
     */
    public async decryptGroupInfo(iv: string, data: string): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/decryptGroupInfo";
        const header = this.getAuthorHeader();
        const body = {iv, data};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 玩家从其他人的分享链接进入.统计分享者的id.
     * @param sourceUid
     * @param scene
     */
    public async loginFromShare(sourceUid: string, scene: string): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/userFromShare";
        const header = this.getAuthorHeader();
        const body = {sourceUid, scene};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 获取当前玩家从分享邀请到的好友列表.
     * @param scene
     * @param newUser 可选参数. 如果为 null,则不考虑newUser参数. 否则严格筛选newUser=true/false.
     * @param limit
     * @param timeLimit
     * @return {
              "code": 0,
              "msgs": "request:ok",
              "data": [
                {
                  "ts": 1243452,
                  "uID": 21,
                  "newUser": true,
                  "nickName": "23df",
                  "avatarUrl": "32r32r234",
                  "gender": 0
                }
              ]
            }
     */
    public async getUsersFromShare(scene: string, newUser?: boolean, limit: number = 10, timeLimit: number = 0): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/wx/api/getUserShares";
        let body = {
            scene,
            limit: limit || 5,
            timeLimit: timeLimit || 0
        };
        if (newUser !== null && newUser !== undefined) {
            body["newUser"] = newUser;
        }
        const header = this.getAuthorHeader();
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    public async getGlobalConfig(): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        const url = API.baseUrl + "/manager/getGlobalConfig";
        const header = this.getAuthorHeader();
        const body = {};
        API.secure.encode(header, body);
        return Wx.post(url, header, body);
    }

    /**
     * 本地向上同步全局服务配置.
     * @param config
     */
    public async uploadGlobalConfig(config: any): Promise<HttpResponse> {
        if (!API.baseUrl) {
            return Promise.resolve(null);
        }
        if (!config) {
            return Promise.resolve(new HttpResponse());
        }
        const url = API.baseUrl + "/manager/updateGlobalConfig";
        const body = {
            "app": API.app,
            "client": API.client,
            "config": JSON.stringify(config),
            "version": API.version,
            "ts": cc.sys.now()
        };
        let secret = API._secretKey;
        for (let bodyKey in body) {
            if (body.hasOwnProperty(bodyKey)) {
                secret += bodyKey;
                secret += body[bodyKey];
            }
        }
        secret += API._secretKey;
        // cc.log("上传配置文件:", secret);
        secret = Base64.encode(secret);
        body["secret"] = md5(secret);
        return Wx.post(url, this.getAuthorHeader(), body, "POST");
    }

    /**
     * 获取鉴权用的Header内容.
     */
    public getAuthorHeader(): any {
        return {
            'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
            "Authorization": "Bearer " + this.token,
            "app": API.app,
            "version": API.version,
            "ts": cc.sys.now(),
            "debug": API.debug
        }
    }

    // 用于合并数据获取请求接口. 防止短时间内大量数据获取请求内容.
    private getUserDataWithInterval(key: string, callback: (resp: HttpResponse) => void) {
        this._userDataCache[key] = callback;
        if (this._lastTimerId) {
            clearTimeout(this._lastTimerId)
        }
        this._lastTimerId = setTimeout(this.getUserDataHandler.bind(this), 1000)
    }

    private getUserDataHandler() {
        this.getUserData(Object.keys(this._userDataCache)).then(resp => {
            for (let key in this._userDataCache) {
                if (this._userDataCache.hasOwnProperty(key)) {
                    this._userDataCache[key](resp)
                    delete this._userDataCache[key]
                }
            }
        })
    }
}

export const wxApi: WxApi = new WxApi();